package org.openmore.platform.wechatOpen;

import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import me.chanjar.weixin.open.api.WxOpenService;
import me.chanjar.weixin.open.api.impl.WxOpenInRedisConfigStorage;
import me.chanjar.weixin.open.api.impl.WxOpenMessageRouter;
import me.chanjar.weixin.open.api.impl.WxOpenServiceImpl;
import me.chanjar.weixin.open.bean.message.WxOpenXmlMessage;
import me.chanjar.weixin.open.bean.result.WxOpenAuthorizerInfoResult;
import me.chanjar.weixin.open.bean.result.WxOpenQueryAuthResult;
import org.apache.commons.lang3.StringUtils;
import org.openmore.common.exception.OpenmoreException;
import org.openmore.platform.dao.PlatformConfigMapper;
import org.openmore.platform.entity.PlatformConfig;
import org.openmore.platform.entity.enums.PlatformType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import redis.clients.jedis.JedisPool;
import tk.mybatis.mapper.entity.Example;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

@Service
public class MyWxOpenServiceImpl implements MyWxOpenService {
    private Logger logger = LoggerFactory.getLogger(getClass());

    private static Map<String, WxOpenService> wxOpenServiceMap;
    private Map<String, WxOpenMessageRouter> wxOpenMessageRouterMap;

    @Autowired
    private JedisPool jedisPool;

    @Autowired
    private PlatformConfigMapper platformConfigMapper;


    /**
     * 单例获得所有的微信配置
     *
     * @return
     */
    public static Map<String, WxOpenService> getInstance() {
        if (wxOpenServiceMap == null) {
            wxOpenServiceMap = new HashMap<>();
        }
        return wxOpenServiceMap;
    }

    @Override
    public WxOpenService getWxOpenService(String tenantId) {
        if (StringUtils.isEmpty(tenantId)) {
            throw new OpenmoreException("租户id不能为空");
        }

        Map<String, WxOpenService> serviceMap = getInstance();
        WxOpenService wxService = serviceMap.get(PlatformType.WECHAT_OPEN+tenantId);
        if (wxService == null) {
//          没有微信服务，则根据tenantid获得对应的微信配置
            Example example = new Example(PlatformConfig.class);
            Example.Criteria criteria = example.createCriteria().andEqualTo("deleted", false)
                    .andEqualTo("platformType", PlatformType.WECHAT_OPEN);
            //多租户模型租户不为空则匹配对应租户配置
            if(!StringUtils.isEmpty(tenantId)){
                criteria.andEqualTo("tenantId", tenantId);
            }
            PlatformConfig wxConfig = platformConfigMapper.selectOneByExample(example);
            if (wxConfig == null) {
                throw new OpenmoreException("找不到租户的微信配置信息");
            }
            WxOpenInRedisConfigStorage inRedisConfigStorage = new WxOpenInRedisConfigStorage(jedisPool);
            inRedisConfigStorage.setComponentAppId(wxConfig.getAppid());
            inRedisConfigStorage.setComponentAppSecret(wxConfig.getSecret());
            inRedisConfigStorage.setComponentToken(wxConfig.getToken());
            inRedisConfigStorage.setComponentAesKey(wxConfig.getAesKey());
//          创建微信服务对象
            wxService = new WxOpenServiceImpl();
            wxService.setWxOpenConfigStorage(inRedisConfigStorage);
            // 缓存起来
            serviceMap.put(PlatformType.WECHAT_OPEN+tenantId, wxService);
            wxOpenMessageRouterMap.put(PlatformType.WECHAT_OPEN+tenantId, newMaRouter(tenantId));
        }
        return wxService;
    }

    /**
     * 获得指定租户id对应的微信服务器回调处理器，使用到了懒加载，只到执行前才添加处理器
     *
     * @param tenantId : 租户ID
     * @return
     */
    private WxOpenMessageRouter newMaRouter(String tenantId) {
        WxOpenMessageRouter router = wxOpenMessageRouterMap.get(PlatformType.WECHAT_OPEN+tenantId);
        if (router == null) {
            router.rule().handler((wxMpXmlMessage, map, wxMpService, wxSessionManager) -> {
                logger.info("\n接收到 {} 公众号请求消息，内容：{}", getWxOpenService(tenantId).getWxOpenConfigStorage().getComponentAppId(), wxMpXmlMessage);
                return null;
            }).next();

            // 缓存起来
            wxOpenMessageRouterMap.put(PlatformType.WECHAT_OPEN+tenantId, router);
        }
        return router;
    }

    public WxOpenMessageRouter getWxOpenMessageRouter(String tenantId){
        return wxOpenMessageRouterMap.get(tenantId);
    }

    /***/
    public String getPreAuthUrl(HttpServletRequest request, String tenantId){
        String host = request.getHeader("host");
        String url = "http://"+host+"/api/auth/jump";
        try {
            url = getWxOpenService(tenantId).getWxOpenComponentService().getPreAuthUrl(url);
            return url;
        } catch (WxErrorException e) {
            logger.error("gotoPreAuthUrl", e);
            throw new OpenmoreException(400, "获取链接出错:"+e.getMessage());
        }
    }

    /**获取授权需要的信息*/
    public WxOpenQueryAuthResult jump(String tenantId, String authorizationCode){
        try {
            WxOpenQueryAuthResult queryAuthResult = getWxOpenService(tenantId).getWxOpenComponentService().getQueryAuth(authorizationCode);
            logger.info("getQueryAuth", queryAuthResult);
            return queryAuthResult;
        } catch (WxErrorException e) {
            logger.error("gotoPreAuthUrl", e);
            throw new OpenmoreException(400, "[code:"+e.getError().getErrorCode()+"]:"+e.getError().getErrorMsg());
        }
    }

    /**获取授权信息*/
    public WxOpenAuthorizerInfoResult getAuthorizerInfo(String tenantId){
        try {
            WxOpenService wxOpenService = getWxOpenService(tenantId);
            String appId = wxOpenService.getWxOpenConfigStorage().getComponentAppId();
            return wxOpenService.getWxOpenComponentService().getAuthorizerInfo(appId);
        } catch (WxErrorException e) {
            logger.error("getAuthorizerInfo", e);
            throw new OpenmoreException(400, "[code:"+e.getError().getErrorCode()+"]:"+e.getError().getErrorMsg());
        }
    }

    /**接收ticket*/
    public Object receiveTick(String tenantId, String requestBody, String timestamp, String nonce, String signature,
                              String encType, String msgSignature){
        this.logger.info(
                "\n接收微信请求：[signature=[{}], encType=[{}], msgSignature=[{}],"
                        + " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ",
                signature, encType, msgSignature, timestamp, nonce, requestBody);
        WxOpenService wxOpenService = getWxOpenService(tenantId);
        if (!StringUtils.equalsIgnoreCase("aes", encType)
                || !wxOpenService.getWxOpenComponentService().checkSignature(timestamp, nonce, signature)) {
            throw new IllegalArgumentException("非法请求，可能属于伪造的请求！");
        }

        // aes加密的消息
        WxOpenXmlMessage inMessage = WxOpenXmlMessage.fromEncryptedXml(requestBody,
                wxOpenService.getWxOpenConfigStorage(), timestamp, nonce, msgSignature);
        this.logger.debug("\n消息解密后内容为：\n{} ", inMessage.toString());
        try {
            String out = wxOpenService.getWxOpenComponentService().route(inMessage);
            this.logger.debug("\n组装回复信息：{}", out);
        } catch (WxErrorException e) {
            this.logger.error("receive_ticket", e);
        }
        return "success";
    }

    /**回调消息*/
    public Object callback(String tenantId, String requestBody,
                           String signature, String timestamp,
                           String nonce, String openid,
                           String encType, String msgSignature) {
        WxOpenService wxOpenService = getWxOpenService(tenantId);
        String appId = wxOpenService.getWxOpenConfigStorage().getComponentAppId();
        this.logger.info(
                "\n接收微信请求：[appId=[{}], openid=[{}], signature=[{}], encType=[{}], msgSignature=[{}],"
                        + " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ",
                appId, openid, signature, encType, msgSignature, timestamp, nonce, requestBody);
        if (!StringUtils.equalsIgnoreCase("aes", encType)
                || !wxOpenService.getWxOpenComponentService().checkSignature(timestamp, nonce, signature)) {
            throw new IllegalArgumentException("非法请求，可能属于伪造的请求！");
        }

        String out = "";
        // aes加密的消息
        WxMpXmlMessage inMessage = WxOpenXmlMessage.fromEncryptedMpXml(requestBody,
                wxOpenService.getWxOpenConfigStorage(), timestamp, nonce, msgSignature);
        this.logger.debug("\n消息解密后内容为：\n{} ", inMessage.toString());
        // 全网发布测试用例
        if (StringUtils.equalsAnyIgnoreCase(appId, "wxd101a85aa106f53e", "wx570bc396a51b8ff8")) {
            try {
                if (StringUtils.equals(inMessage.getMsgType(), "text")) {
                    if (StringUtils.equals(inMessage.getContent(), "TESTCOMPONENT_MSG_TYPE_TEXT")) {
                        out = WxOpenXmlMessage.wxMpOutXmlMessageToEncryptedXml(
                                WxMpXmlOutMessage.TEXT().content("TESTCOMPONENT_MSG_TYPE_TEXT_callback")
                                        .fromUser(inMessage.getToUser())
                                        .toUser(inMessage.getFromUser())
                                        .build(),
                                wxOpenService.getWxOpenConfigStorage()
                        );
                    } else if (StringUtils.startsWith(inMessage.getContent(), "QUERY_AUTH_CODE:")) {
                        String msg = inMessage.getContent().replace("QUERY_AUTH_CODE:", "") + "_from_api";
                        WxMpKefuMessage kefuMessage = WxMpKefuMessage.TEXT().content(msg).toUser(inMessage.getFromUser()).build();
                        wxOpenService.getWxOpenComponentService().getWxMpServiceByAppid(appId).getKefuService().sendKefuMessage(kefuMessage);
                    }
                } else if (StringUtils.equals(inMessage.getMsgType(), "event")) {
                    WxMpKefuMessage kefuMessage = WxMpKefuMessage.TEXT().content(inMessage.getEvent() + "from_callback").toUser(inMessage.getFromUser()).build();
                    wxOpenService.getWxOpenComponentService().getWxMpServiceByAppid(appId).getKefuService().sendKefuMessage(kefuMessage);
                }
            } catch (WxErrorException e) {
                logger.error("callback", e);
            }
        }else{
            WxMpXmlOutMessage outMessage = getWxOpenMessageRouter(tenantId).route(inMessage, appId);
            if(outMessage != null){
                out = WxOpenXmlMessage.wxMpOutXmlMessageToEncryptedXml(outMessage, wxOpenService.getWxOpenConfigStorage());
            }
        }
        return out;
    }
}